// ====== 数组循环方法 ======

const arr = ['苹果', '香蕉', '橙子'];

// 1. for循环 (传统)
// i 是索引(index)
for (let i = 0; i < arr.length; i++) {
    const value = arr[i]; // 通过索引获取值
    console.log(`索引: ${i}, 值: ${value}`);
}

// 2. for...of (ES6)
// item 直接是值(value)
for (const item of arr) {
    console.log(`值: ${item}`);
}

// 如果需要索引，可以使用entries()
for (const [index, value] of arr.entries()) {
    console.log(`索引: ${index}, 值: ${value}`);
}
console.log('-----------------------',arr.entries());
// 3. for...in (不推荐用于数组，但可以使用)
// index 是索引(index)
for (const index in arr) {
    const value = arr[index]; // 通过索引获取值
    console.log(`索引: ${index}, 值: ${value}`);
}
// 注意：for...in会遍历数组的所有可枚举属性，包括原型链上的属性，不推荐用于数组
// 但可以用于对象属性的遍历
// 例如：
const obj = { a: 1, b: 2, c: 3 };
// for...in 可以遍历对象的属性
for (const key in obj) {
    console.log(`键: ${key}, 值: ${obj[key]}`);
}
// 但是不推荐用于数组，因为它可能会遍历到原型链上的属性
// ====== 数组方法循环 ======

// 3. forEach
// 参数顺序: 值, 索引, 原数组
arr.forEach((value, index, array) => {
    console.log(`值: ${value}, 索引: ${index}`);
});

// forEach只使用一个参数时，它代表值
arr.forEach(value => {
    console.log(`值: ${value}`);
});

// 4. map (返回新数组)
const numbers = [1, 2, 3];
// 参数顺序: 值, 索引, 原数组
const doubled = numbers.map((value, index, array) => {
    console.log(`处理: 值=${value}, 索引=${index}`);
    return value * 2;
});

// map只使用一个参数时，它代表值
const simpleDoubled = numbers.map(value => value * 2);

// 5. filter (过滤返回新数组)
const nums = [1, 2, 3, 4, 5];
// 参数顺序: 值, 索引, 原数组
const filtered = nums.filter((value, index, array) => {
    console.log(`检查: 值=${value}, 索引=${index}`);
    return value > 2;
});

// filter只使用一个参数时，它代表值
const evenNumbers = nums.filter(value => value % 2 === 0);

// 6. reduce (累加器)
const reduceArr = [1, 2, 3, 4];
// 参数顺序: 累加器, 当前值, 当前索引, 原数组
const sum = reduceArr.reduce((accumulator, currentValue, index, array) => {
    console.log(`累加器=${accumulator}, 当前值=${currentValue}, 索引=${index}`);
    return accumulator + currentValue;
}, 0); // 0是初始值
console.log('sum',sum);

// reduce只使用两个参数时，它们代表累加器和当前值
const product = reduceArr.reduce((acc, val) => acc * val, 1);
console.log('product',product);
// 示例：数组扁平化
const flattened = [[0, 1], [2, 3], [4, 5]].reduce(
  (acc, val) => acc.concat(val), []
); // [0, 1, 2, 3, 4, 5]
console.log('flattened',flattened);

// 7. some (检查是否至少有一个元素满足条件),只要一个满足条件的就退出循环，返回布尔
const someArr = [1, 2, 3, 4, 5];
// 参数顺序: 值, 索引, 原数组
const hasEven = someArr.some((value, index, array) => {
    console.log(`检查: 值=${value}, 索引=${index}`);
    return value % 2 === 0;
});
console.log(`数组中是否有偶数: ${hasEven}`);


// 8. every (检查是否所有元素都满足条件)，只要一个不满足就退出循环,返回布尔
const everyArr = [2, 4, 8, 8];
// 参数顺序: 值, 索引, 原数组
const allEven = everyArr.every((value, index, array) => {
    console.log(`检查: 值=${value}, 索引=${index}`);
    return value % 2 === 0;
});
console.log(`数组中的所有元素是否都是偶数: ${allEven}`);


// 9. find (返回第一个满足条件的元素)
const findArr = [1, 3, 5, 8, 10];
// 参数顺序: 值, 索引, 原数组
const firstEven = findArr.find((value, index, array) => {
    console.log(`检查: 值=${value}, 索引=${index}`);
    return value % 2 === 0;
});

// 10. findIndex (返回第一个满足条件的元素的索引)
// 参数顺序: 值, 索引, 原数组
const firstEvenIndex = findArr.findIndex((value, index, array) => {
    console.log(`检查: 值=${value}, 索引=${index}`);
    return value % 2 === 0;
});

// ====== 对象循环方法 ======

const person = {
    name: '张三',
    age: 30,
    job: '工程师'
};

// 1. for...in (遍历对象的可枚举属性)以及原型链上的可枚举属性，但不会遍历不可枚举属性。
// key 是属性名(键)
for (const key in person) {
    const value = person[key]; // 通过键获取值
    console.log(`键: ${key}, 值: ${value}`);
}

// 使用hasOwnProperty过滤掉继承属性
for (const key in person) {
    if (person.hasOwnProperty(key)) {
        console.log(`自有属性 - 键: ${key}, 值: ${person[key]}`);
    }
}

// 2. Object.keys() + forEach
// 获取所有键的数组，然后遍历
Object.keys(person).forEach(key => {
    console.log(`键: ${key}, 值: ${person[key]}`);
});

// 3. Object.values() + forEach
// 直接遍历所有值
Object.values(person).forEach(value => {
    console.log(`值: ${value}`);
});

// 4. Object.entries() + forEach
// 遍历键值对数组
Object.entries(person).forEach(([key, value],index,array) => {
    console.log(`键: ${key}, 值: ${value},hh:${index},shuzu:${array}`);
});
console.log('entries',Object.entries(person));
// 5. Object.entries() + for...of
// 使用解构赋值获取键和值
for (const [key, value] of Object.entries(person)) {
    console.log(`键: ${key}, 值: ${value}`);
}
//关于知识点：
// for...in 是唯一会遍历原型链上可枚举属性的循环方// for...in 是唯一会遍历原型链上可枚举属性的循环方法，使用时通常需要配合 hasOwnProperty 来过滤掉原型链属性
// 法，使用时通常需要配合 hasOwnProperty 来过滤掉原型链属性
// 如果需要遍历对象的不可枚举属性，应使用 PropertObject.getOwnyNames() 或 Reflect.ownKeys()

// 1. for...in循环：会遍历对象自身的可枚举属性以及原型链上的可枚举属性，但不会遍历不可枚举属性。

// 2. Object.keys()：只返回对象自身的可枚举属性，不包括原型链上的属性和不可枚举属性。

// 3. Object.getOwnPropertyNames()：返回对象自身的所有属性（包括不可枚举属性），但不包括原型链上的属性。

// 4. for...of：用于遍历可迭代对象，不涉及属性枚举问题。遍历可迭代对象（如数组、字符串、Map、Set等），返回的是元素值

// 5. forEach、map、filter等数组方法：只处理数组元素，不涉及属性枚举问题。



//检查是不是数组/对象/
// arguments用法
// 获取元素得方法有哪些
// 5. 检查变量类型
function checkType(variable) {
    if (Array.isArray(variable)) {
        console.log('变量是数组');
    } else if (typeof variable === 'object' && variable !== null) {
        console.log('变量是对象');
    } else {
        console.log(`变量类型: ${typeof variable}`);
    }
}

// 6. arguments用法示例
function exampleArguments() {
    console.log('arguments对象:', arguments);
    for (let i = 0; i < arguments.length; i++) {
        console.log(`参数${i}:`, arguments[i]);
    }
}

// 7. 获取元素的方法
function getElementMethods() {
    // DOM元素获取方法
    const byId = document.getElementById('elementId');
    const byClass = document.getElementsByClassName('className');
    const byTag = document.getElementsByTagName('div');
    const byQuery = document.querySelector('.selector');
    const byQueryAll = document.querySelectorAll('div.className');
    
    console.log('通过ID获取:', byId);
    console.log('通过类名获取:', byClass);
    console.log('通过标签获取:', byTag);
    console.log('通过query选择器获取:', byQuery);
    console.log('通过queryAll选择器获取:', byQueryAll);
}
